import asyncio
import time
import socket
import threading
from abc import abstractmethod
import logging
from datetime import timedelta
from homeassistant.helpers.event import async_track_time_interval
_LOGGER = logging.getLogger(__name__)

class sockClient(threading.Thread):

    def __init__(self, host, port,hass):
        threading.Thread.__init__(self)
        self.host = host
        self.port = port
        self._hass = hass
        self._onmessage = None
        self._open_flag = False
        self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    def sock_connect(self):
        try:
            self.client.connect((self.host, self.port))
            self._open_flag = True
        except Exception as e:
            time.sleep(3)
            self.sock_connect()

    def send(self, data):
        try:
            self.client.send(data)
        except:
            self._open_flag = False
            time.sleep(3)
            self.sock_connect()

    async def close(self):
        self._open_flag = False
        await self._hass.async_add_executor_job(
            self.client.close
        )
        self.join(1)

    def handle_read(self):
        while True:
            data = self.client.recv(1024)
            if data == b'':
                self._open_flag = False
                time.sleep(3)
                self.sock_connect()
            else:
                self.onmessage(data)

    @abstractmethod
    def onmessage(self, data):
        pass

    def run(self):
        self.sock_connect()
        self.handle_read()


class xkClient(sockClient):
    """信科客户端"""
    def __init__(self, host, port,hass,lights):
        super().__init__(host, port , hass)
        self.lock = threading.Lock()
        data = [ conf['slave'] for conf in lights ]
        self._poll = [ i for n, i in enumerate(data) if i not in data[:n]]
        self._cb = []
           

    async def req_state(self,slave):
        """请求状态"""
        data = [0x55,slave,0x10,0,0,0,0,0]
        data[-1] = self.data_add(data)
        await self._hass.async_add_executor_job(
            self.send,bytes(data)
        )

    def handle_state(self, data):
        """处理状态"""
        state = []
        bits = data[5]*256+data[6]
        for i in range(16):
            state.append(True if bits >> i & 1 else False)
        if len(self._cb)>0:
            for cb in self._cb:
                cb(state,data[1])

    async def poll_state(self,e):
        if not self._open_flag:
            return
        for slave in self._poll:
            await self.req_state(slave)
            await asyncio.sleep(1)
        

    async def start_hub(self) -> None:

        async_track_time_interval(
                self._hass, self.poll_state, timedelta(seconds=5)
            )
        try:
            await self._hass.async_add_executor_job(
                self.start
            )
        except Exception as e:
            _LOGGER.error(e)
            await self._hass.async_add_executor_job(
                self.sock_connect
            )
    
    def add_callback(self,cb):
        self._cb.append(cb)

    def write_cmd(self,slave,address,cmd):
        """写入命令"""
        data = [0x55,slave,0x31,0,0,0,address,0]
        if cmd:
            data[2] = 0x32
        data[-1] = self.data_add(data)
        self.send(bytes(data))

    def onmessage(self, data):
        if not self.check(data):
            """累加校验失败"""
            return False
        if data[0] == 34 and data[2] == 16:  # 状态反馈数据
            self.handle_state(data)

    def check(self, data):
        """累加校验信科"""
        return self.data_add(data) == data[-1]

    def data_add(self,data):
        """累加"""
        sum1 = 0
        for i in range(len(data)-1):
            sum1 = sum1 + data[i]
        return sum1 & 0xff

config = {
    'dev':{
        'host':'192.168.1.200',
        'port':4198,
        'props':[
            {
            'slave': 1,
            'address': 2
            },
            {
            'slave': 2,
            'address': 2
            },
            {
            'slave': 3,
            'address': 2
            },
            {
            'slave': 4,
            'address': 2
            }
        ]
    }
}

class PollThread(threading.Thread):
    """轮询线程"""
    def __init__(self,poll,client) -> None:
        threading.Thread.__init__(self)
        self.poll = poll
        self.client = client
    def run(self):
        while True:
            for slave in self.poll:
                time.sleep(1)
                self.client.req_state(slave)

    
